package com.itany.corejava.code08_常用类.object;

/**
 * @author 石小俊
 * @date 2023年04月24日 10:18
 */
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
//        getClassTest();
//        equalsTest();
//        hashCodeTest();
//        toStringTest();
//        cloneTest();
        finalizeTest();
    }

    public static void finalizeTest() {
        Person person = new Person();
        Person person2 = person;
        person = null;
        person2 = null;
        // 建议执行gc
        // System.gc方法底层就是Runtime.getRuntime().gc()
        // System.gc();
        // 该方法是一个本地方法
        Runtime.getRuntime().gc();
    }

    public static void cloneTest() throws CloneNotSupportedException {
        Address address = new Address("江苏", "南京");
        Person person = new Person("admin", 20, address);
        System.out.println(person);

        // 如果直接在Person中重写clone方法
        // 调用后执行会报错,错误为:CloneNotSupportedException
        // 表示克隆不被支持的异常
        // 因此,对于克隆方法,克隆的对象必须满足以下要求
        // 1.克隆的类必须实现Cloneable接口
        // 2.克隆的类必须重写clone方法, 方法中直接调用Object提供的克隆方法即可。即:super.clone()
        Object obj = person.clone();
        if (obj instanceof Person) {
            Person person2 = (Person) obj;
            // 此时打印结果,发现person2与person完全一致
            System.out.println(person2);

            System.out.println("*****************开始修改*****************");
            // 对于非对象类型,类似于值传递,直接克隆
            // 当克隆后的对象发生修改时,对克隆前的对象没有影响
            person2.setName("jack");
            person2.setAge(18);
            // 对于对象类型,类似于引用传递
            // 克隆的值是引用的对象的地址值
            // 当克隆后的对象发生修改时,本质上修改的是该地址所对应的堆内存中数据的变更
            // 克隆前的对象引用的是同一个堆内存区域中的数据
            // 因此,当克隆后的对象发生修改时,克隆前的对象也随之改变
            person2.getAddress().setProvince("安徽");
            person2.getAddress().setCity("合肥");
            System.out.println(person);
            System.out.println(person2);
        }
    }

    public static void toStringTest() {
        Person person = new Person("admin", 20);
        Person person2 = new Person("admin", 18);
        System.out.println(person);
        System.out.println(person.toString());
        System.out.println(person2);
    }

    public static void hashCodeTest() {
        Person person = new Person("admin", 20);
        Person person2 = new Person("admin", 20);
        Person person3 = new Person("admin", 18);
        System.out.println(person.equals(person2));
        System.out.println(person.equals(person3));
        System.out.println(person.hashCode());
        System.out.println(person.hashCode());
        System.out.println(person2.hashCode());
        System.out.println(person3.hashCode());
    }

    public static void equalsTest() {
        System.out.println("***************默认的equals方法***************");
        Person person = new Person("admin", 18);
        Person person2 = new Person("admin", 18);
        System.out.println(person == person2);      // false
        System.out.println(person.equals(person2)); // false
        int i = 1, i2 = 1;
        System.out.println(i == i2);    // true
        System.out.println("***************String中的equlas方法***************");
        String s1 = new String("admin");
        String s2 = new String("admin");
        // 通过new的方式指定字符串,表示在堆中开辟了一块空间
        // 此时在堆中开辟了两块空间用于存储s1与s2
        // 因此,其栈中的地址值不同,通过==比较的是栈中地址的值,所以为false
        System.out.println(s1 == s2);       // false
        // 字符串重写了equals方法,此时比较的是堆中具体的字符串值,忽略了地址
        System.out.println(s1.equals(s2));  // true
        System.out.println("***************开发者手动重写equals方法***************");
        System.out.println(person == person2);      // false
        // 重写equals方法后,比较name与age属性的值时,其值为true
        System.out.println(person.equals(person2));
        Person person3 = new Person("admin", 20);
        System.out.println(person.equals(person3));
    }

    public static void getClassTest() {
        Person person = new Person();
        // 所有的类都直接或者间接继承了Object
        // 因此,都能调用Object所提供的方法
        // getClass方法用于返回运行时类型
        // 返回的数据格式: class 包名.类名
        // 此时返回时发现结果是Man
        System.out.println(person.getClass());

        // 多态,父类的引用指向了子类的对象
        // 此时的编译时类型:Person
        // 此时的运行时类型:Man
        Person person2 = new Man();
        System.out.println(person2.getClass());

        // 此时如果想要调用子类方法,需要向下转型
        if (person2 instanceof Man) {
            Man man = (Man) person2;
            man.work();
        }

        // 此时也可以通过运行时类型进行判断
        // 每一个类都有一个class属性,其属性值即为当前类的class类型
        // 语法:类名.class
        System.out.println(Person.class);
        System.out.println(Man.class);

        Person person3 = new WoMan();
        System.out.println(person3.getClass());

        // 当某个对象的运行时类型与某个类的class类型一致时,说明是同一个类型
        // 此时可以强制类型转换
        if (person2.getClass() == Man.class) {
            Man man = (Man) person2;
            man.work();
        }
        System.out.println(person3.getClass() == Man.class);
    }

}
